/*------------------------------------------------------------------------------

 Copyright 2007 Sony Corporation

------------------------------------------------------------------------------*/
/**
 @file    uart.c

          This file provides .

 @author  Luc Fourestier
 @version 0.1
 @date    03/2007

*/
/*----------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------
 Includes
------------------------------------------------------------------------------*/

#include "types.h"
#include "h_300.h"
#include "sys.h"
#include "h300_irq.h"
#include "assert.h"
#include "tx.h"

#include "uart.h"

/*------------------------------------------------------------------------------
 Defines
------------------------------------------------------------------------------*/

/* interrupt mechanism */
#define INTERRUPT_BASED

/* Data reg */
#define     DR_F_ERR        0x0100
#define     DR_P_ERR        0x0200
#define     DR_B_ERR        0x0400
#define     DR_O_ERR        0x0800
#define     DATA_MASK       0x00FF
#define     DR_ERR_MASK     0x0F00

/* Status Reg */
#define     SR_F_ERR        0x0001
#define     SR_P_ERR        0x0002
#define     SR_B_ERR        0x0004
#define     SR_O_ERR        0x0008
#define     SR_ERR_MASK     0x000F

/* UARTFR Reg */
#define     FR_CTS          0x0001
#define     FR_DSR          0x0002
#define     FR_DCD          0x0004
#define     FR_BUSY         0x0008
#define     FR_RXFE         0x0010
#define     FR_TXFF         0x0020
#define     FR_RXFF         0x0040
#define     FR_TXFE         0x0080
#define     FR_RI           0x0100
#define     FR_MASK         0x01FF

/* Line Control Reg */
#define     LCR_BRK         0x0001
#define     LCR_PEN         0x0002
#define     LCR_EPS         0x0004
#define     LCR_STP2        0x0008
#define     LCR_FEN         0x0010
#define     LCR_WLEN5       0x0000
#define     LCR_WLEN6       0x0020
#define     LCR_WLEN7       0x0040
#define     LCR_WLEN8       0x0060
#define     LCR_WLEN_MASK   0x0060
#define     LCR_SPS         0x0080
#define     LCR_MASK        0x00FF

/* Control Reg */
#define     CR_UARTEN       0x0001
#define     CR_SIREN        0x0002
#define     CR_SIRLP        0x0004
#define     CR_LBE          0x0080
#define     CR_TXE          0x0100
#define     CR_RXE          0x0200
#define     CR_DTR          0x0400
#define     CR_RTS          0x0800
#define     CR_OUT1         0x1000
#define     CR_OUT2         0x2000
#define     CR_RTSE         0x4000
#define     CR_CTSE         0x8000
#define     CR_MASK         0xFF87

/* Fifo Reg */
#define     FF_1_8          0x0000
#define     FF_1_4          0x0001
#define     FF_1_2          0x0002
#define     FF_3_4          0x0003
#define     FF_7_8          0x0004
#define     FF_TX(x)        (x)
#define     FF_RX(x)        ((x)<<3)
#define     FF_MASK         0x003f

/* Interrupt Regs */
#define     IRQ_RI          0x0001
#define     IRQ_CTS         0x0002
#define     IRQ_DCD         0x0004
#define     IRQ_DSR         0x0008
#define     IRQ_RX          0x0010
#define     IRQ_TX          0x0020
#define     IRQ_RT          0x0040
#define     IRQ_FE          0x0080
#define     IRQ_PE          0x0100
#define     IRQ_BE          0x0200
#define     IRQ_OE          0x0400
#define     IRQ_MASK        0x07FF

#define     DRV_TX_IRQ_MASK     (IRQ_TX)
#define     DRV_RX_IRQ_MASK     (IRQ_RX | IRQ_RT)
#define     DRV_IRQ_MASK        (DRV_TX_IRQ_MASK | DRV_RX_IRQ_MASK)



/*------------------------------------------------------------------------------
 Statics
------------------------------------------------------------------------------*/

static TX_MUTEX UartProtectionMutex;
static TX_SEMAPHORE UartTxSem;
static TX_SEMAPHORE UartRxSem;
static void WaitTxCompletion(void);
static uint8_t WaitRx(uint32_t waitOption);


/*------------------------------------------------------------------------------
 Functions
------------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/**
 I.

 @param  NONE
 @return NONE
*/
/*----------------------------------------------------------------------------*/
void uart_Initialise(void)
{
    uint32_t baud_x128;

    /* uart access protection */
    tx_mutex_create(&UartProtectionMutex, "UartProtection", TX_INHERIT);

    tx_semaphore_create(&UartTxSem, "UartTxSem", 0);
    tx_semaphore_create(&UartRxSem, "UartRxSem", 0);

    /* Calculate baud rate divisor using integer maths.
     * A bit of factorisation has been performed to prevent
     * overflow.
     *
     *  Calculation:
     *
     *  1 +  Clock_Freq_kHz *1000 *128
     *       -------------------------
     *          baud_rate * 16
     *
     * Multiply by 128 to give 7 fractional bits in integer
     *  result and add 1 to round bottom bit.
     * Divide by 128 to give integer part of baud rate divider.
     * Divide by 2 and mask bottom six bits to give fractional
     *  part of baud rate divider.
     *
     */

    baud_x128 = 1+((Sys_GetClockkHz() *80)/(1152));             /* calculate baud divisor * 128 and round */

    tx_mutex_get(&UartProtectionMutex, TX_WAIT_FOREVER);

    p_UART->UARTIBRD  = baud_x128 /128;                         /* Integer part */
    p_UART->UARTFBRD  = (baud_x128 /2) & 0x3f;                  /* Fractional part */


    p_UART->UARTLCR_H = LCR_WLEN8;      /* 8 bit, no parity, fifos disabled */
    p_UART->UARTCR = (CR_RXE | CR_TXE | CR_UARTEN);     /* Enable Uart, TX & RX */

    /* unmask RX and timeout irq - yea I know its called a `mask' register.... */
    p_UART->UARTIMSC = DRV_RX_IRQ_MASK;

    /*p_UART->UARTIFLS = (FF_TX(FF_3_4) | FF_RX(FF_3_4));            set tx-rx fifo size @ 3/4 (12) */
    /*p_UART->UARTLCR_H |= LCR_FEN;                                    enable fifos */

/*    p_UART->UARTIMSC |= DRV_TX_IRQ_MASK;*/        /* enable TX IRQ */

    tx_mutex_put(&UartProtectionMutex);

    VIC_enable_irqs(C_VIC_UART_IRQ);

    return;
}



/*----------------------------------------------------------------------------*/
/**
 I.

 @param  NONE
 @return NONE
*/
/*----------------------------------------------------------------------------*/
int uart_Write(char c)
{
    int result = 0;

    tx_mutex_get(&UartProtectionMutex, TX_WAIT_FOREVER);

    /* write a character to the FIFO buffer */
    if (p_UART->UARTFR & FR_TXFE)
    {
        p_UART->UARTDR = c;

        WaitTxCompletion();

        result = 1;
    }

    tx_mutex_put(&UartProtectionMutex);

    return result;
}



/*----------------------------------------------------------------------------*/
/**
 I.

 @param  NONE
 @return NONE
*/
/*----------------------------------------------------------------------------*/
int uart_Read(char *c, uint32_t waitOption)
{
    int result = 0;

    result = (int) WaitRx(waitOption);
    if (result)
    {
        tx_mutex_get(&UartProtectionMutex, TX_WAIT_FOREVER);
        if (p_UART->UARTFR & FR_RXFF)
        {
            /* read a character from the FIFO buffer */
            *c = (char)p_UART->UARTDR;

            result = 1;
        }
        tx_mutex_put(&UartProtectionMutex);
    }

    return result;
}



/*----------------------------------------------------------------------------*/
/**
 I.

 @param  NONE
 @return NONE
*/
/*----------------------------------------------------------------------------*/
void uart_InterruptHandler(void)
{
    uint8_t interrupt;

    interrupt = (p_UART->UARTMIS) & DRV_IRQ_MASK;

    if (interrupt & DRV_RX_IRQ_MASK)
    {
        /* disable UART receive interrupts */
        p_UART->UARTIMSC &= ~DRV_RX_IRQ_MASK;;

        /* signal that we have data waiting to be read */
        tx_semaphore_put(&UartRxSem);
    }

    if (interrupt & DRV_TX_IRQ_MASK)
    {
        /* disable UART transmit interrupts */
        p_UART->UARTIMSC &= ~DRV_TX_IRQ_MASK;

        /* signal the transmission complete */
        tx_semaphore_put(&UartTxSem);
    }

    p_UART->UARTICR = interrupt;

    return;
}



/*----------------------------------------------------------------------------*/
/**
 I.

 @param  NONE
 @return NONE
*/
/*----------------------------------------------------------------------------*/
static void WaitTxCompletion(void)
{
#ifdef INTERRUPT_BASED
    /* enable Tx interrupt */
    p_UART->UARTIMSC |= DRV_TX_IRQ_MASK;

    tx_semaphore_get(&UartTxSem, TX_WAIT_FOREVER);
#else
    while (!(p_UART->UARTFR & FR_TXFE))
    {
    }
#endif
}



/*----------------------------------------------------------------------------*/
/**
 I.

 @param  NONE
 @return NONE
*/
/*----------------------------------------------------------------------------*/
static uint8_t WaitRx(uint32_t waitOption)
{
    uint8_t retVal = 1; /* OK */

#ifdef INTERRUPT_BASED
    /* enable Rx interrupt */
    tx_mutex_get(&UartProtectionMutex, TX_WAIT_FOREVER);
    p_UART->UARTIMSC |= DRV_RX_IRQ_MASK;
    tx_mutex_put(&UartProtectionMutex);

    if (TX_SUCCESS != (tx_semaphore_get(&UartRxSem, (ULONG) waitOption)))
    {
        retVal = 0;
    }
#else
    while (!(p_UART->UARTFR & FR_RXFF))
    {
    }
#endif

    return(retVal);
}

